﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using Ooganizer.Platform.Win32;
using Ooganizer.Server;

namespace Ooganizer.Launcher
{
    class ShellExecuteLauncher : ILauncher
    {
        enum Strategy
        {
            ePIDFilename,
            eFilepath
        }

        private Strategy getStrategy(string location)
        {
            // TODO Need to implement this and determine which strategy to return for a particular location
            return Strategy.ePIDFilename;
        }

        public static Guid Id = new Guid("7A6ED4C9-63F0-4a53-9AB5-0DA2C7FA93B4");

        public Guid GetId()
        {
            return Id;
        }

        public IntPtr Launch(string location)
        {
            IntPtr hWnd = IntPtr.Zero;

            Strategy strategy = getStrategy(location);
            switch (strategy)
            {
                case Strategy.ePIDFilename:
                    {
                        IntPtr hProcess = OpenEx(location);
                        int iPid = Win32Functions.GetProcessId(hProcess);

                        Win32Functions.EnumWindowsContext_pid_filename context = new Win32Functions.EnumWindowsContext_pid_filename();
                        context.targetPid = iPid;
                        context.filePath = location;

                        Win32Functions.EnumWindowsProc proc = new Win32Functions.EnumWindowsProc(Win32Functions.EnumWindowProcImpl_pid_filename);

                        hWnd = Win32Functions.GetWindowHandle(proc, context);

                        if (hWnd == IntPtr.Zero)
                        {
                            // TODO: Need to call generic resolver and try to find the window handle of the app that
                            // contains the possibly already opened instance Perhaps, (like in word) the initial 
                            // process returned by OpenEx was closed once it (the app) determined that the document 
                            // was already loaded in another instance of the app
                            //
                            // If the Generic resolver cannot find it, notify user that the artifact could not be loaded.

                            Log.Error(string.Format("{0}() - Found process but couldn't find window handle: strategy = {1} filePath = {2} pid = {3} hWnd = {4}",
                                                    System.Reflection.MethodBase.GetCurrentMethod().Name, strategy, location, iPid, hWnd));
                        }
                        else
                        {
                            Log.Info(string.Format("{0}() - strategy = {1} filePath = {2} hWnd = {3}",
                                                   System.Reflection.MethodBase.GetCurrentMethod().Name, strategy, location, hWnd));
                        }

                        break;
                    }
                case Strategy.eFilepath:
                    {
                        // This strategy may be more accurate, but less useful: it does not check the pid and not all apps 
                        // include the full path in the title bar
                        IntPtr hProcess = OpenEx(location);

                        Win32Functions.EnumWindowsContext_filePath context = new Win32Functions.EnumWindowsContext_filePath();
                        context.filePath = location;

                        Win32Functions.EnumWindowsProc proc = new Win32Functions.EnumWindowsProc(Win32Functions.EnumWindowProcImpl_filePath);

                        hWnd = Win32Functions.GetWindowHandle(proc, context);

                        Log.Info(string.Format("{0}() - strategy = {1} filePath = {2} hWnd = {3}",
                                               System.Reflection.MethodBase.GetCurrentMethod().Name, strategy, location, hWnd));

                        break;
                    }
                default:
                    {
                        Log.Error(string.Format("{0}() - Invalid strategy: {1)",
                                               System.Reflection.MethodBase.GetCurrentMethod().Name, strategy));
                        
                        break;
                    }
            }

            if (hWnd != IntPtr.Zero)
            {
                Show(hWnd, WindowAction.SW_SHOWNORMAL);
            }
            else
            {
                Log.Info(string.Format("{0}() - Could not load filePath {0} with strategy {1}",
                                       System.Reflection.MethodBase.GetCurrentMethod().Name, location, strategy));
            }            

            return hWnd;
        }

        // Searches Registry to find what to open the file with (default file handler)
        static string FindExe(string file)
        {
            StringBuilder retStr = new StringBuilder();

            int ret = (int)Win32Functions.FindExecutable(file, null, retStr);

            return (retStr.ToString());
        }       

        // Functions uses the ShellExecuteEx Method to start a process
        static IntPtr OpenEx(string file)
        {
            string strExe = FindExe(file);
            
            // If the file contains dll or no exe found, try to use explorer
            if (strExe.Length == 0 || strExe.Contains("dll"))
            {                
                // Get windows path
                StringBuilder windowsPath = new StringBuilder();
                Win32Functions.SHGetFolderPath(IntPtr.Zero, 
                                               (int)CSIDL.CSIDL_WINDOWS, 
                                               IntPtr.Zero, 
                                               (int)SHGFP_TYPE.SHGFP_TYPE_CURRENT, 
                                               windowsPath);

                strExe = '\"' + windowsPath.ToString() + "\\explorer.exe" + '\"';
            }            

            const int SEE_MASK_NOCLOSEPROCESS = 0x40;                        

            ShellExecuteInfo sei = new ShellExecuteInfo();
            sei.cbSize = Marshal.SizeOf(sei);
            sei.lpVerb = "open";
            sei.lpFile = strExe;
            sei.lpParameters = '\"' + file + '\"';
            sei.nShow = (int)WindowAction.SW_SHOWNORMAL;
            sei.fMask = SEE_MASK_NOCLOSEPROCESS;

            if (!Win32Functions.ShellExecuteEx(ref sei))
                throw new System.ComponentModel.Win32Exception();

            Win32Functions.WaitForSingleObject(sei.hProcess, (uint)500);

            return sei.hProcess;
        }

        static bool Show(IntPtr wHandle, WindowAction action)
        {
            return (Win32Functions.ShowWindow(wHandle, (int)action) != 0);
        }

        #region log4net declaration

        private log4net.ILog Log
        {
            get
            {
                if (_logWrapper == null)
                {
                    _logWrapper = new LogServerClientWrapper(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
                }

                return _logWrapper.Log;
            }
        }

        private LogServerClientWrapper _logWrapper;

        #endregion

    }
}
